﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace tina
{
    internal class Program
    {
        public uint current_state = 0;
        public int current_left = 0;
        public int current_top = 0;
        public int edit_left = 0;
        public int edit_top = 0;
        public string cmd = "";
        public string op = "";
        public string NORMAL = "-- NORMAL -- op:";
        public string INSERT = "-- INSERT --";
        public List<string> text;

        public Program()
        {
            this.text = new List<string>();
            this.text.Add("");
        }

        private void writeState(string state_str)
        {
            this.edit_left = Console.CursorLeft;
            this.edit_top = Console.CursorTop;
            Console.CursorLeft = 0;
            Console.CursorTop = Console.WindowHeight - 1;
            for (int i = 0; i < Console.WindowWidth - 1; ++i)
            {
                Console.Write(" ");
            }
            Console.CursorLeft = 0;
            Console.Write(state_str);
            Console.CursorLeft = this.edit_left;
            Console.CursorTop = this.edit_top;
        }

        public void addString(int y, int x, string str)
        {
            int left = Console.CursorLeft;
            int top = Console.CursorTop;
            Console.CursorLeft = x;
            Console.CursorTop = y;
            Console.Write(str);
            Console.CursorLeft = left;
            Console.CursorTop = top;
        }

        public void writeKey(char k)
        {
            int left;
            switch (this.current_state)
            {
                case 1:
                    int top = this.current_top + Console.CursorTop;
                    left = this.current_left + Console.CursorLeft;
                    this.text[top] = this.text[top].Insert(left, k.ToString());
                    Console.CursorLeft++;
                    for (int line_num = 0; line_num < Console.WindowHeight - 2; line_num++)
                    {
                        if (line_num >= this.text.Count)
                        {
                            break;
                        }
                        else
                        {
                            this.addString(line_num, 0, this.text[line_num]);
                        }
                    }
                    break;
                case 2:
                    if (this.cmd.Length < Console.WindowWidth - 2)
                    {
                        left = Console.CursorLeft - 1;
                        this.cmd = this.cmd.Insert(left, k.ToString());
                        this.addString(Console.WindowHeight - 1, 1, this.cmd);
                        Console.CursorLeft++;
                    }
                    break;
            }
        }

        public void removeKey(int index, bool is_cursor_back = true)
        {
            if (this.current_state == 0 || this.current_state == 1)
            {
                int text_top = this.current_top + Console.CursorTop;
                if (text_top >= 0 && text_top <= this.text.Count - 1 &&
                    index >= 0 && index <= this.text[text_top].Length - 1)
                {
                    int left = Console.CursorLeft;
                    Console.CursorLeft = 0;
                    for (int i = 0; i < this.text[text_top].Length; ++i)
                    {
                        Console.Write(" ");
                    }
                    Console.CursorLeft = left;
                    this.text[text_top] = this.text[text_top].Remove(index, 1);
                    this.addString(Console.WindowHeight - 1, 1, this.text[text_top]);
                    if (is_cursor_back)
                    {
                        Console.CursorLeft--;
                    }
                }
            }
        }

        public void removeCmdKey(int cmd_index, bool is_cursor_back = true)
        {
            if (this.current_state == 2)
            {
                if (cmd_index >= 0 && cmd_index <= this.cmd.Length - 1)
                {
                    this.cmd = this.cmd.Remove(cmd_index, 1);
                    int left = Console.CursorLeft;
                    int top = Console.CursorTop;
                    Console.CursorLeft = 1;
                    Console.CursorTop = Console.WindowHeight - 1;
                    for (int i = 0; i < Console.WindowWidth - 2; ++i)
                    {
                        Console.Write(" ");
                    }
                    Console.CursorLeft = left;
                    Console.CursorTop = top;
                    this.addString(Console.WindowHeight - 1, 1, this.cmd);
                    if (is_cursor_back)
                    {
                        Console.CursorLeft--;
                    }
                }
            }
        }

        public void moveCmdCursorHome()
        {
            if (this.current_state == 2)
            {
                Console.CursorLeft = 1;
            }
        }

        public void moveCmdCursorEnd()
        {
            if (this.current_state == 2)
            {
                Console.CursorLeft = this.cmd.Length + 1;
            }
        }

        public void moveCursor(int x_distance, int y_distance)
        {
            switch (this.current_state)
            {
                case 0:
                case 1:
                    break;
                case 2:
                    if (Console.CursorLeft == 1 &&
                        x_distance > 0 &&
                        this.cmd.Length > 0)
                    {
                        Console.CursorLeft += x_distance;
                    }
                    else if (Console.CursorLeft > 1)
                    {
                        if (x_distance < 0)
                        {
                            Console.CursorLeft += x_distance;
                        }
                        else if (x_distance > 0 &&
                            this.cmd.Length + 1 > Console.CursorLeft)
                        {
                            Console.CursorLeft += x_distance;
                        }
                    }
                    break;
            }
        }

        public void setState(uint state)
        {
            this.current_state = state;
            this.cmd = "";
            this.op = "";
            switch (state)
            {
                case 0:
                    this.writeState(this.NORMAL);
                    break;
                case 1:
                    this.writeState(this.INSERT);
                    break;
                case 2:
                    this.writeState(":");
                    Console.CursorLeft = 1;
                    Console.CursorTop = Console.WindowHeight - 1;
                    break;
            }
        }

        public void writeOp()
        {
            this.addString(Console.WindowHeight - 1, 0, this.NORMAL + this.op);
            Console.CursorLeft = this.NORMAL.Length + this.op.Length;
            Console.CursorTop = Console.WindowHeight - 1;
        }

        static void Main(string[] args)
        {
            Console.Clear();
            Program p = new Program();
            p.setState(0);
            ConsoleKeyInfo k = Console.ReadKey(true);
            char[] state1 = {'i', 'I', 'a', 'A', 'o', 'O'};
            char[] state0_double = {'y', 'g'};
            char state0_first = ' ';
            uint[] skip_keys = {
                (uint)ConsoleKey.F1,
                (uint)ConsoleKey.F2,
                (uint)ConsoleKey.F3,
                (uint)ConsoleKey.F4,
                (uint)ConsoleKey.F5,
                (uint)ConsoleKey.F6,
                (uint)ConsoleKey.F7,
                (uint)ConsoleKey.F8,
                (uint)ConsoleKey.F9,
                (uint)ConsoleKey.F10,
                (uint)ConsoleKey.F11,
                (uint)ConsoleKey.F12
            };
            while (true)
            {
                if (skip_keys.Contains((uint)k.Key))
                {
                    k = Console.ReadKey(true);
                    continue;
                }
                if (p.current_state == 0)
                {
                    if (k.Key == ConsoleKey.Escape)
                    {
                        string space = "";
                        for (int i = 0; i < p.op.Length; i++)
                        {
                            space += " ";
                        }
                        p.addString(Console.WindowHeight - 1, 0, space);
                        p.op = "";
                        p.setState(0);
                        Console.CursorLeft = p.NORMAL.Length + p.op.Length;
                        Console.CursorTop = Console.WindowHeight - 1;
                    }
                    else if (k.KeyChar == ':')
                    {
                        p.setState(2);
                    }
                    else if (state1.Contains(k.KeyChar))
                    {
                        if (p.op.Length == 0)
                        {
                            p.setState(1);
                            Console.CursorLeft = 0;
                            Console.CursorTop = 0;
                        }
                        else
                        {
                            p.op += k.KeyChar;
                            p.writeOp();
                        }
                    }
                    else if (k.KeyChar >= '0' && k.KeyChar <= '9')
                    {
                        p.op += k.KeyChar;
                        p.writeOp();
                    }
                    else if (state0_double.Contains(k.KeyChar))
                    {
                        if (state0_first == ' ')
                        {
                            state0_first = k.KeyChar;
                            p.op += k.KeyChar;
                        }
                        else if (state0_first == k.KeyChar)
                        {
                            string space = "";
                            for (int i = 0; i < p.op.Length; i++)
                            {
                                space += " ";
                            }
                            p.addString(Console.WindowHeight - 1, 0, space);
                            p.op = "";
                            state0_first = ' ';
                        }
                        else
                        {
                            p.op += k.KeyChar;
                            p.writeOp();
                        }
                    }
                }
                else if (p.current_state == 1)
                {
                    switch (k.Key)
                    {
                        case ConsoleKey.Escape:
                            p.setState(0);
                            break;
                        case ConsoleKey.Enter:
                            Console.CursorLeft = 0;
                            if (Console.CursorTop < Console.WindowHeight - 2)
                            {
                                Console.CursorTop++;
                            }
                            else
                            {
                                p.current_top++;
                            }
                            p.text.Add("");
                            break;
                        case ConsoleKey.Backspace:
                            p.removeKey(Console.CursorLeft);
                            break;
                        case ConsoleKey.Delete:
                            p.removeKey(Console.CursorLeft, false);
                            break;
                        default:
                            p.writeKey(k.KeyChar);
                            break;
                    }
                }
                else if (p.current_state == 2)
                {
                    switch (k.Key)
                    {
                        case ConsoleKey.Escape:
                            p.setState(0);
                            break;
                        case ConsoleKey.LeftArrow:
                            p.moveCursor(-1, 0);
                            break;
                        case ConsoleKey.RightArrow:
                            p.moveCursor(1, 0);
                            break;
                        case ConsoleKey.Backspace:
                            p.removeCmdKey(Console.CursorLeft - 2);
                            break;
                        case ConsoleKey.Delete:
                            p.removeCmdKey(Console.CursorLeft - 1, false);
                            break;
                        case ConsoleKey.Home:
                            p.moveCmdCursorHome();
                            break;
                        case ConsoleKey.End:
                            p.moveCmdCursorEnd();
                            break;
                        case ConsoleKey.Enter:
                            break;
                        case ConsoleKey.PageUp:
                        case ConsoleKey.PageDown:
                        case ConsoleKey.Tab:
                        case ConsoleKey.UpArrow:
                        case ConsoleKey.DownArrow:
                            break;
                        default:
                            p.writeKey(k.KeyChar);
                            break;
                    }
                }
                k = Console.ReadKey(true);
            }
        }
    }
}
